package com.floreantpos.model.dao;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.floreantpos.PosException;
import com.floreantpos.model.Gratuity;
import com.floreantpos.model.Terminal;
import com.floreantpos.model.Ticket;
import com.floreantpos.model.TipsCashoutReport;
import com.floreantpos.model.User;
import com.floreantpos.model.TipsCashoutReport.TipsCashoutReportData;
import com.floreantpos.model.util.DateUtil;

public class GratuityDAO extends BaseGratuityDAO {

  /**
   * Default constructor. Can be used in place of getInstance()
   */
  public GratuityDAO() {
  }

  public List<Gratuity> findGratuityByTicket(Ticket ticket) throws PosException {
    Session session = null;

    try {
      session = getSession();

      Criteria criteria = session.createCriteria(getReferenceClass());
      criteria.add(Restrictions.eq(Gratuity.PROP_TICKET, ticket));

      return criteria.list();
    } catch (Exception e) {
      throw new PosException("Ocorreu um erro durante a busca das Gratificações");
    } finally {
      closeSession(session);
    }
  }

  public List<Gratuity> findByUser(User user) throws PosException {
    Session session = null;

    try {
      session = getSession();

      Criteria criteria = session.createCriteria(getReferenceClass());
      criteria.add(Restrictions.eq(Gratuity.PROP_OWNER, user));
      criteria.add(Restrictions.eq(Gratuity.PROP_PAID, Boolean.FALSE));
      criteria.add(Restrictions.ne(Gratuity.PROP_AMOUNT, Double.valueOf(0)));

      return criteria.list();
    } catch (Exception e) {
      throw new PosException("Ocorreu um erro durante a busca das Gratificações do Atendente " + user.getFirstName() + " " + user.getLastName());
    } finally {
      closeSession(session);
    }
  }

  public List<Gratuity> findUnpaidGratuitiesByUser(User user) throws PosException {
    Session session = null;

    try {
      session = getSession();

      Criteria criteria = session.createCriteria(getReferenceClass());
      criteria.createAlias(Gratuity.PROP_TICKET, "ticket");
      criteria.add(Restrictions.eq("ticket." + Ticket.PROP_CLOSED, Boolean.TRUE));
      criteria.add(Restrictions.eq(Gratuity.PROP_OWNER, user));
      criteria.add(Restrictions.eq(Gratuity.PROP_PAID, Boolean.FALSE));
      criteria.add(Restrictions.ne(Gratuity.PROP_AMOUNT, Double.valueOf(0)));

      return criteria.list();
    } catch (Exception e) {
      throw new PosException("Ocorreu um erro durante a busca das Gratificações do Atendente " + user.getFirstName() + " " + user.getLastName());
    } finally {
      closeSession(session);
    }
  }

  public void payGratuities(List<Gratuity> gratuities) {
    Session session = null;
    Transaction tx = null;

    double total = 0;
    try {
      session = getSession();
      tx = session.beginTransaction();
      for (Gratuity gratuity : gratuities) {
        total = gratuity.getAmount();
        gratuity.setPaid(true);
        session.saveOrUpdate(gratuity);

        Terminal terminal = gratuity.getTerminal();
        terminal.setCurrentBalance(terminal.getCurrentBalance() - gratuity.getAmount());
        session.saveOrUpdate(terminal);
      }

      tx.commit();
    } catch (Exception e) {
      if (tx != null) {
        tx.rollback();
      }
      throw new PosException("Ocorreu um erro ao registrar o pagamento das Gratificações");
    } finally {
      closeSession(session);
    }
  }

  public TipsCashoutReport createReport(Date fromDate, Date toDate, User user) {
    Session session = null;

    try {
      session = getSession();

      fromDate = DateUtil.startOfDay(fromDate);
      toDate = DateUtil.endOfDay(toDate);

      Criteria criteria = session.createCriteria(Ticket.class);
      criteria.add(Restrictions.eq(Ticket.PROP_OWNER, user));
      criteria.add(Restrictions.eq(Ticket.PROP_CLOSED, Boolean.TRUE));
      criteria.add(Restrictions.ge(Ticket.PROP_CREATE_DATE, fromDate));
      criteria.add(Restrictions.le(Ticket.PROP_CREATE_DATE, toDate));

      List list = criteria.list();

      TipsCashoutReport report = new TipsCashoutReport();
      report.setServer(user.getUserId() + "/" + user.toString());
      report.setFromDate(fromDate);
      report.setToDate(toDate);
      report.setReportTime(new Date());

      for (Iterator iter = list.iterator(); iter.hasNext();) {
        Ticket ticket = (Ticket) iter.next();

        TipsCashoutReportData data = new TipsCashoutReportData();
        data.setTicketId(ticket.getId());
        data.setSaleType(ticket.getCardType());
        data.setTicketTotal(ticket.getTotalAmount());

        if (ticket.getGratuity() != null) {
          Double tipsAmount = Double.valueOf(0);

          Criteria gratuityCriteria = session.createCriteria(getReferenceClass());
          gratuityCriteria.add(Restrictions.eq(Gratuity.PROP_TICKET, ticket));

          List gratuityList = gratuityCriteria.list();

          for (Iterator gratuityIter = gratuityList.iterator(); gratuityIter.hasNext();) {
            Gratuity gratuity = (Gratuity) gratuityIter.next();
            tipsAmount += gratuity.getAmount();
          }
          data.setTips(tipsAmount);
          data.setPaid(ticket.getGratuity().isPaid().booleanValue());
        } else {
          data.setTips(Double.valueOf(0));

        }
        report.addReportData(data);
      }
      report.calculateOthers();
      return report;
    } catch (Exception e) {
      throw new PosException("Ocorreu um erro durante a busca das Gratificações do Atendente " + user.getFirstName() + " " + user.getLastName());
    } finally {
      closeSession(session);
    }

  }
}
